#ifndef __PROTOBUFFIK_SAVE_LOAD_H_
#define __PROTOBUFFIK_SAVE_LOAD_H_

#include "protobuffik_stream.h"
#include "protobuffik_string.h"
#include "protobuffik_results.h"


namespace protobuffik
{

// =====
// save
// =====
template< typename Type >
inline
size_t save( ostream& stream, const Type& value )
{
	return value.save( stream );
}


inline
size_t save( ostream& stream, uint_8 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, int_8 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, uint_16 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, int_16 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, uint_32 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, int_32 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, uint_64 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, int_64 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, float_32 value )
{
	stream << value;
	return sizeof( value );
}


inline
size_t save( ostream& stream, float_64 value )
{
	stream << value;
	return sizeof( value );
}


// =====
// load
// =====
template< typename Type >
inline
LoadingResult load( istream& stream, Type& valueOut, size_t& bytesLoaded )
{
	return valueOut.load( stream, bytesLoaded );
}


inline
LoadingResult load( istream& stream, uint_8& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, int_8& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, uint_16& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, int_16& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, uint_32& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, int_32& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, uint_64& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, int_64& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, float_32& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


inline
LoadingResult load( istream& stream, float_64& valueOut, size_t& bytesLoaded )
{
	size_t valueSize = sizeof( valueOut );
	if( stream.get_unread_length( ) < valueSize )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	stream >> valueOut;

	bytesLoaded = valueSize;
	
	return LOADING_RESULT_OK;
}


// =====
// count
// =====
template< uint_32 MaxCount >
inline
size_t get_count_length( )
{
	if( MaxCount <= 0xFF )
		return 1;
	else if( MaxCount <= 0xFFFF )
		return 2;
	return 4;
}


template< uint_32 MaxCount >
inline
size_t save_count( ostream& stream, uint_32 value )
{
	size_t s = 0;
	if( MaxCount <= 0xFF )
	{
		stream << (uint_8) value;
		s = sizeof( uint_8 );
	}
	else if( MaxCount <= 0xFFFF )
	{
		stream << (uint_16) value;
		s = sizeof( uint_16 );
	}
	else
	{
		stream << (uint_32) value;
		s = sizeof( uint_32 );
	}

	return s;
}


template< uint_32 MaxCount >
inline
LoadingResult load_count( istream& stream, uint_32& valueOut, size_t& bytesLoaded )
{
	size_t s = 0;
	if( MaxCount <= 0xFF )
		s = sizeof( uint_8 );
	else if( MaxCount <= 0xFFFF )
		s = sizeof( uint_16 );
	else
		s = sizeof( uint_32 );

	if( stream.get_unread_length( ) < s )
		return LOADING_RESULT_ERROR_UNEXPECTED_END_OF_STREAM;

	if( MaxCount <= 0xFF )
	{
		uint_8 v = 0;
		stream >> v;

		valueOut = v;
	}
	else if( MaxCount <= 0xFFFF )
	{
		uint_16 v = 0;
		stream >> v;

		valueOut = v;
	}
	else
	{
		uint_32 v = 0;
		stream >> v;

		valueOut = v;
	}

	bytesLoaded = s;

	return LOADING_RESULT_OK;
}


template< uint_32 MaxCount >
inline
LoadingResult load_count( istream& stream, uint_32& valueOut )
{
	size_t bytesLoaded = 0;
	return load_count< MaxCount >( stream, valueOut, bytesLoaded );
}

};

#endif